package com.ams.protocol.rtmp;

import java.io.IOException;
import java.util.HashMap;

import com.ams.io.BufferInputStream;

public class RtmpHeaderDeserializer {
    private HashMap<Integer, RtmpHeader> chunkHeaderMap;
    private BufferInputStream in;

    public RtmpHeaderDeserializer(BufferInputStream in) {
        super();
        this.in = in;
        this.chunkHeaderMap = new HashMap<Integer, RtmpHeader>();
    }

    private RtmpHeader getLastHeader(int chunkStreamId) {
        RtmpHeader h = chunkHeaderMap.get(chunkStreamId);
        if (h == null) {
            h = new RtmpHeader(chunkStreamId, 0, 0, 0, 0);
            chunkHeaderMap.put(chunkStreamId, h);
        }
        return h;
    }

    public RtmpHeader read() throws IOException {
        int h = in.readByte() & 0xFF; // Chunk Basic Header
        int chunkStreamId = h & 0x3F; // 1 byte version
        if (chunkStreamId == 0) { // 2 byte version
            chunkStreamId = in.readByte() & 0xFF + 64;
        } else if (chunkStreamId == 1) { // 3 byte version
            chunkStreamId = in.read16BitLittleEndian() + 64;
        }

        RtmpHeader lastHeader = getLastHeader(chunkStreamId);
        int fmt = h >>> 6;
        long ts = 0;
        if (fmt == 0) { // type 0 header
            ts = in.read24Bit();
            lastHeader.setTimestamp(ts); // absolute timestamp
        }
        long lastTimestamp = lastHeader.getTimestamp();
        if (fmt == 1 || fmt == 2) { // type 1, type 2 header
            ts = in.read24Bit();
            lastHeader.setTimestamp(lastTimestamp + ts); // delta timestamp
        }
        if (fmt == 0 || fmt == 1) { // type 0, type 1 header
            int size = in.read24Bit();
            lastHeader.setSize(size);
            lastHeader.setType(in.readByte() & 0xFF);
        }
        if (fmt == 0) { // type 0 header
            int streamId = (int) in.read32BitLittleEndian();
            lastHeader.setStreamId(streamId);
        }
        if (fmt == 3) { // type 3
            // 0 bytes
        }

        // extended time stamp
        if (fmt == 0 && ts >= 0x00FFFFFF) {
            ts = in.read32Bit();
            lastHeader.setTimestamp(ts);
        }
        if ((fmt == 1 || fmt == 2) && ts >= 0x00FFFFFF) {
            ts = in.read32Bit();
            lastHeader.setTimestamp(lastTimestamp + ts);
        }

        return new RtmpHeader(chunkStreamId, lastHeader.getTimestamp(),
                lastHeader.getSize(), lastHeader.getType(),
                lastHeader.getStreamId());
    }

}
